/***********************************
* Name: MainPanel.java
* Date: Aug 7, 2010
* @author martin
* Description:
***********************************/

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JRootPane;
import javax.swing.JToggleButton;
import javax.swing.event.MouseInputAdapter;

public class MainPanel extends JPanel{


    private MousePanel  panel_center = new MousePanel();
    private JPanel      panel_east = new JPanel();
    private JPanel      panel_south = new JPanel();
    private JToolBar    toolBar;
    private JLabel      mousePosition = new JLabel();
    private JLabel      selectionGateInfo = new JLabel();
    private JLabel      selectionGateInputA = new JLabel();
    private JLabel      selectionGateInputB = new JLabel();

    public static HashMap     gatesHashMap = new LinkedHashMap();
    private LinkedList<Gate> gates = new LinkedList<Gate>();
    private int         entry_len = 6;

    private JToggleButton pointer;
    private JToggleButton not_button;
    private JToggleButton and_button;
    private JToggleButton nor_button;
    private JToggleButton or_button;
    private JToggleButton nand_button;

    /*****************************************
    * MainPanel
    * Constructor
    *****************************************/
    public MainPanel(){

        this.setLayout(new BorderLayout());
        InitEastPanel();
        InitCenterPanel();
        InitSouthPanel();

        //this.add(toolBar, BorderLayout.EAST);
        this.add(this.panel_east, BorderLayout.EAST);
        this.add(this.panel_center, BorderLayout.CENTER);
        this.add(this.panel_south, BorderLayout.SOUTH);

        this.setAutoscrolls(true);
        this.setPreferredSize(new Dimension(800,600));
        this.setBackground(Color.white);
    }

    /*****************************************
     * InitEastPanel
     * @return void
     * Inicialize the components of the east panel
     *****************************************/
    private void InitEastPanel(){
        this.panel_east = new JPanel();

        //************************ Actions ************************
        Action actionPointer = new AbstractAction("Pointer", Main.iconPointer) {
          public void actionPerformed(ActionEvent e) {
              System.out.println("pointer");
              click_pointer();
          }
        };
        Action actionNOT = new AbstractAction("NOT", Main.iconNOT) {
          public void actionPerformed(ActionEvent e) {
              System.out.println("not");
              pointer.setSelected(false);
              not_button.setSelected(true);
              and_button.setSelected(false);
              nor_button.setSelected(false);
              or_button.setSelected(false);
              nand_button.setSelected(false);
          }
        };
        Action actionAND = new AbstractAction("AND", Main.iconAND) {
            public void actionPerformed(ActionEvent e) {
              System.out.println("and");
              pointer.setSelected(false);
              not_button.setSelected(false);
              and_button.setSelected(true);
              nor_button.setSelected(false);
              or_button.setSelected(false);
              nand_button.setSelected(false);

            }
        };
        Action actionOR = new AbstractAction("OR", Main.iconOR) {
            public void actionPerformed(ActionEvent e) {
              System.out.println("or");
              pointer.setSelected(false);
              not_button.setSelected(false);
              and_button.setSelected(false);
              nor_button.setSelected(false);
              or_button.setSelected(true);
              nand_button.setSelected(false);
            }
        };
        Action actionNAND = new AbstractAction("NAND", Main.iconNAND) {
            public void actionPerformed(ActionEvent e) {
              System.out.println("nand");
              pointer.setSelected(false);
              not_button.setSelected(false);
              and_button.setSelected(false);
              nor_button.setSelected(false);
              or_button.setSelected(false);
              nand_button.setSelected(true);
            }
        };
        Action actionNOR = new AbstractAction("NOR", Main.iconNOR) {
            public void actionPerformed(ActionEvent e) {
              System.out.println("nor");
              pointer.setSelected(false);
              not_button.setSelected(false);
              and_button.setSelected(false);
              nor_button.setSelected(true);
              or_button.setSelected(false);
              nand_button.setSelected(false);
            }
        };
        //*********************************************************
        //*********************** JToolBar ************************
        toolBar = new JToolBar();
        //---------------------------------------------
        this.pointer = new JToggleButton(actionPointer);
        this.pointer.setText("");
        this.pointer.setToolTipText("Select");
        this.pointer.setSelected(true);
        toolBar.add(pointer);
        //---------------------------------------------
        toolBar.addSeparator();
        //---------------------------------------------
        this.not_button = new JToggleButton(actionNOT);
        this.not_button.setText("");
        this.not_button.setToolTipText("NOT gate");
        toolBar.add(this.not_button);
        //---------------------------------------------
        this.and_button = new JToggleButton(actionAND);
        this.and_button.setText("");
        this.and_button.setToolTipText("AND gate");
        toolBar.add(this.and_button);
        //---------------------------------------------
        this.or_button = new JToggleButton(actionOR);
        this.or_button.setText("");
        this.or_button.setToolTipText("OR gate");
        toolBar.add(this.or_button);
        //---------------------------------------------
        this.nand_button = new JToggleButton(actionNAND);
        this.nand_button.setText("");
        this.nand_button.setToolTipText("NAND gate");
        toolBar.add(this.nand_button);
        //---------------------------------------------
        this.nor_button = new JToggleButton(actionNOR);
        this.nor_button.setText("");
        this.nor_button.setToolTipText("NOR gate");
        toolBar.add(this.nor_button);
        //---------------------------------------------
        //*********************************************************
        toolBar.setBorderPainted(true);
        toolBar.setBackground(Color.white);
        toolBar.setVisible(true);
        this.toolBar.setOrientation(JToolBar.VERTICAL);
        this.panel_east.add(toolBar);
        this.panel_east.setBackground(Color.white);
        this.panel_east.setVisible(true);
    }

    /*****************************************
     * InitSouthPanel
     * @return void
     * Inicialize the components of the south panel
     *****************************************/
    private void InitSouthPanel(){
        this.panel_south = new JPanel();
        this.panel_south.setLayout(new GridLayout(1,5));
        this.mousePosition = new JLabel("< , >");
        selectionGateInfo = new JLabel("");
        selectionGateInputA = new JLabel("");
        selectionGateInputB = new JLabel("");
        this.panel_south.add(this.selectionGateInfo);
        this.panel_south.add(this.selectionGateInputA);
        this.panel_south.add(this.selectionGateInputB);
        this.panel_south.add(new JLabel(""));
        this.panel_south.add(this.mousePosition);
        this.panel_south.setBackground(Color.white);
    }

    /*****************************************
     * InitCenterPanel
     * @return void
     * Inicialize the components of the center panel
     *****************************************/
    private void InitCenterPanel(){
        this.panel_center = new MousePanel();
        this.getPanel_center().setBackground(Color.white);

    }

    /*****************************************
     * click_pointer
     * @return void
     * When the pointer click is pressed
     *****************************************/
    private void click_pointer() {
        pointer.setSelected(true);
        not_button.setSelected(false);
        and_button.setSelected(false);
        nor_button.setSelected(false);
        or_button.setSelected(false);
        nand_button.setSelected(false);
    }

    /*****************************************
     * @return the gates
     *****************************************/
    public LinkedList<Gate> getGates() {
        return gates;
    }

    /*****************************************
     * isEndGate
     * @param gate
     * @return boolean
     * Check if the gate is end (output)
     *****************************************/
    public boolean isOutputGate(Gate gate){

        for(Gate a : this.gates){
            System.out.println("    Gate: "+a.getName()+" type: "+Gate.getTypeString(a.getType()));
            if(a.getType() == Gate.NOT){
                if(a.getA().getFrom().compareTo(gate.getName())==0){
                    return false;
                }
            }
            else{
                if(a.getA().getFrom().compareTo(gate.getName())==0){
                    return false;
                }
                if(a.getB().getFrom().compareTo(gate.getName())==0){
                    return false;
                }
            }
        }
        return true;
    }

    /*****************************************
     * @param gates the gates to set
     *****************************************/
    public void setGates(LinkedList<Gate> gates) {

        this.gatesHashMap.clear();
        this.gates = gates;
        
        //hashmap contains a tuple <String Name : Gate gate >
        for(Gate a : gates){
            a.setOutput(this.isOutputGate(a));
            this.gatesHashMap.put(a.getName(), a);
        }
    }

    /*****************************************
     * @param gates the gates to set
     *****************************************/
    public Gate getGateHashValue(String name) {

        boolean contains = this.gatesHashMap.containsKey(name);

        if(contains){
            return (Gate)this.gatesHashMap.get(name);
        }
        return null;
    }

    /*****************************************
     * @param name
     * @return boolean
     *****************************************/
    public static boolean existGate(String name) {

        boolean contains = gatesHashMap.containsKey(name);
        if(contains){
            return true;
        }
        return false;
    }

    /*****************************************
     * @return the panel_center
     *****************************************/
    public JPanel getPanel_center() {
        return panel_center;
    }

    /*****************************************
     * @return int
     *****************************************/
    public int getToolButtonSelected(){

        if(this.not_button.isSelected())
            return Gate.NOT;
        if(this.and_button.isSelected())
            return Gate.AND;
        if(this.nand_button.isSelected())
            return Gate.NAND;
        if(this.or_button.isSelected())
            return Gate.OR;
        if(this.nor_button.isSelected())
            return Gate.NOR;

        return Gate.POINTER;
    }

    private class MousePanel extends JPanel implements MouseListener, MouseMotionListener {

        private int x = -1;
        private int y = -1;

        /*****************************************
        * MousePanel
        * Constructor
        *****************************************/
        public MousePanel(){
            addMouseMotionListener(this);
            addMouseListener(this);
        }

        /*****************************************
        * mouseClick
        * @param me
        * @return void
        *****************************************/
        public void mouseClicked(MouseEvent me) {
            System.out.println("mouseClicked");

            Gate gate = mouseInGate(me);
            int toolButtonSelected = getToolButtonSelected();
            String gate_text = "";
            if(gate==null){
                if(toolButtonSelected == Gate.POINTER){
                    panel_center.repaint();
                    return ;
                }
                //************* new gate ***************************************
                gate = new Gate();
                gate.setX(me.getX());
                gate.setY(me.getY());
                gate.setType(toolButtonSelected);
                
                gate = dialogGate(gate,true);
                if(gate!=null){
                    gates.add(gate);
                    setGates(gates);
                    click_pointer();
                    //JOptionPane.showMessageDialog(this,"The gate created","Creating gate",JOptionPane.INFORMATION_MESSAGE);
                }
                //**************************************************************
                panel_center.repaint();
                return;
            }
            //click on a gate (open it) if pointer is selected
            //else -> do nothing
            if(toolButtonSelected == Gate.POINTER){
                System.out.println("Open a gate: "+gate.getName());
                //****************** open gate *********************************
                int index_of_gate = gates.indexOf(gate);
                String gate_name = gate.getName();
                System.out.println("index_of_gate: "+index_of_gate);
                gate = dialogGate(gate,false);
                //if gate is null, delete it from list
                if(gate==null){
                    System.out.println("Removing the gate");
                    gates.remove(index_of_gate);
                    removeAllInput(gate_name);
                    setGates(gates);
                    panel_center.repaint();
                    JOptionPane.showMessageDialog(this,"The gate was removed: ","Removing gate",JOptionPane.INFORMATION_MESSAGE);
                    
                }
                //**************************************************************
                panel_center.repaint();
                return ;
            }
            JOptionPane.showMessageDialog(this,"Can't create a new gate\nbecause there is another here","Can't create a gate",JOptionPane.INFORMATION_MESSAGE);
            panel_center.repaint();
        }

        /*****************************************
        * mousePressed
        * @param me
        * @return void
        *****************************************/
        public void mousePressed(MouseEvent me) {
            System.out.println("mousePressed");
        }

        /*****************************************
        * mouseReleased
        * @param me
        * @return void
        *****************************************/
        public void mouseReleased(MouseEvent me) {
            System.out.println("mouseReleased");
        }

        /*****************************************
        * mouseEntered
        * @param me
        * @return void
        *****************************************/
        public void mouseEntered(MouseEvent me) {
            System.out.println("mouseEntered");
        }

        /*****************************************
        * mouseExited
        * @param me
        * @return void
        *****************************************/
        public void mouseExited(MouseEvent me) {
            System.out.println("mouseExited");
            //x = me.getX();
            //y = me.getY();
            mousePosition.setText("< -1"+","+"-1 >");
        }

        /*****************************************
        * mouseDragged
        * @param me
        * @return void
        *****************************************/
        public void mouseDragged(MouseEvent me) {
            System.out.println("mouseDragged");

            Gate gate = mouseInGate(me);
            int toolButtonSelected = getToolButtonSelected();

            if(toolButtonSelected==Gate.POINTER && gate!=null){
                gate.setX(me.getX()-55/2);
                gate.setY(me.getY()-25/2);
                panel_center.repaint();
            }
            

        }

        /*****************************************
        * mouseMoved
        * @param me
        * @return void
        *****************************************/
        public void mouseMoved(MouseEvent me) {
            //System.out.println("mouseMoved");
            Gate gate = mouseInGate(me);

            selectionGateInfo.setText("");
            selectionGateInputA.setText("");
            selectionGateInputB.setText("");

            if(gate!=null){
                String gate_text = "";
                gate_text = " Gate: "+gate.getName()+"  Type: "+gate.getTypeString(gate.getType());
                selectionGateInfo.setText(gate_text);
                selectionGateInputA.setText("  A: from "+gate.getA().getFrom()+", ("+gate.getA().getFallingDelay()+"/"+gate.getA().getRaisingDelay()+")");
                if(gate.getType() != Gate.NOT){
                    selectionGateInputB.setText("B: from "+gate.getB().getFrom()+", ("+gate.getB().getFallingDelay()+"/"+gate.getB().getRaisingDelay()+")");
                }else{
                    selectionGateInputB.setText("");
                }
                
            }
            mousePosition.setText("< "+me.getX()+","+me.getY()+" >");
        }

        /*****************************************
        * getComboOptions
        * @return JComboBox
        * Return a predefined comboBox
        *****************************************/
        public JComboBox getComboOptions(){
            JComboBox combo = new JComboBox();

            for(int i=0;i<entry_len;i++){
                combo.addItem("E"+(i+1));
            }

            for(Gate a : gates){
                combo.addItem(a.getName());
            }

            return combo;
        }

        /*****************************************
        * removeAllInput
        * @param gate_name
        * @return void
        * Remove all the references in the input gate
        *****************************************/
        private void removeAllInput(String gate_name) {
            for(Gate a: gates){
                if(a.getA().getFrom().compareToIgnoreCase(gate_name)==0){
                    a.getA().setFrom("E1");
                }
                if(a.getType()!=Gate.NOT){
                    if(a.getB().getFrom().compareToIgnoreCase(gate_name)==0){
                        a.getB().setFrom("E1");
                    }
                }
            }
        }

        /*****************************************
        * dialogGate
        * @param gate
        * @return Gate
        * return the gate modified showing a dialogBox
        *****************************************/
        public Gate dialogGate(Gate gate, boolean isNew){

            //--- parameters ------------------------------------
            String gateName = gate.getName();
            String gateType = gate.getTypeString(gate.getType());
            int xCoord = gate.getX();
            int yCoord = gate.getY();
            JComboBox sentFromA = getComboOptions();
            String fromA = gate.getA().getFrom();
            sentFromA.setSelectedItem(fromA);
            int fallDelayA = gate.getA().getFallingDelay();
            int raiDelayA = gate.getA().getRaisingDelay();
            JComboBox sentFromB;
            int fallDelayB;
            int raiDelayB;
            if(gate.getType()==Gate.NOT){
                sentFromB = new JComboBox();
                fallDelayB = 0;
                raiDelayB = 0;
            }
            else{
                sentFromB = getComboOptions();
                sentFromB.setSelectedItem(gate.getB().getFrom());
                fallDelayB = gate.getB().getFallingDelay();
                raiDelayB = gate.getB().getRaisingDelay();
            }
            //boolean end = gate.isOutput();


            JFrame jframe = Main.frame;
            Frame frame = (Frame)jframe;

            //---------------------------------------------------
            CustomDialog dialog = new CustomDialog(frame, gateName, gateType, xCoord, yCoord, sentFromA, fallDelayA, raiDelayA, sentFromB, fallDelayB, raiDelayB);
            //---------------------------------------------------
            if(dialog.cancelbutton){
                if(isNew){
                    return null;
                }
                return gate;
            }
            else if(dialog.gateDeleted){
                return null;
            }
            else if(dialog.okbutton){
                //X
                gate.setX((Integer)dialog.coordX.getValue());
                //Y
                gate.setY((Integer)dialog.coordY.getValue());
                //INPUTA
                //Falling
                gate.getA().setFallingDelay((Integer)dialog.fdA.getValue());
                //Raising
                gate.getA().setRaisingDelay((Integer)dialog.rdA.getValue());
                //From
                gate.getA().setFrom((String)dialog.fromA.getSelectedItem());
                if(gate.getType()!=Gate.NOT){
                    //Falling
                    gate.getB().setFallingDelay((Integer)dialog.fdB.getValue());
                    //Raising
                    gate.getB().setRaisingDelay((Integer)dialog.rdB.getValue());
                    //From
                    gate.getB().setFrom((String)dialog.fromB.getSelectedItem());
                }
                //end
                //gate.setOutput(dialog.isfinal.isSelected());
            }
            return gate;
        }

        /*****************************************
        * mouseInGate
        * @param me
        * @return gate
        * returns the Gate if the mouse is over it
        *****************************************/
        private Gate mouseInGate(MouseEvent me) {
            for(Gate a : gates){
                if(a.getX()<me.getX() && me.getX()<(a.getX()+55) && a.getY()<me.getY() && me.getY()<(a.getY()+25)){
                    return a;
                }
            }
            return null;
        }

        /*****************************************
        * paintComponent
        * @param g
        * @return void
        *****************************************/
        public void paintComponent(Graphics g) {
            System.out.println("repainting the center panel");
            super.paintComponent(g);

            //--- Draw entry ----------
            drawEntry(g);
            //--- Draw gates ----------
            for(Gate a : gates){
                drawGate(a, g);
            }
            //--- Draw lines ----------
            for(Gate a : gates){
                drawLines(a, g);
            }
        }

        /*****************************************
        * drawGate
        * @param gate
        * @param g
        * @return void
        * Draw one gate in the panel
        *****************************************/
        private void drawGate(Gate gate, Graphics g) {
            ImageIcon imageicon = Gate.getImageGate(gate.getType());
            Image image = imageicon.getImage();
            
            g.setFont(new Font("Arial", Font.BOLD, 12) );
            g.drawImage(image, gate.getX(), gate.getY(), this);
            g.drawString("     "+gate.getName(), gate.getX(), 15 + gate.getY()+imageicon.getIconHeight());//+
        }

        /*****************************************
        * drawEntry
        * @param g
        * @return void
        * Draw all the entries in the panel
        *****************************************/
        private void drawEntry(Graphics g) {

            g.setFont(new Font("Arial", Font.BOLD, 12) );
            for(int i =0;i<entry_len;i++){
                int a = panel_center.getHeight()/(entry_len+1)*(i+1);
                g.drawString("  E"+(i+1)+" -", 0, a);
            }
        }

        /*****************************************
        * drawLines
        * @param gate
        * @param g
        * @return void
        * Draw the lines for the gate input A and B
        *****************************************/
        private void drawLines(Gate gate, Graphics g) {

            if(gate.getType()==Gate.NOT){
                //draw just one line
                //--- input A ---------
                Point initial = new Point(gate.getX(), gate.getY()+12);
                Point end = new Point();
                if(gate.getA().getFrom().startsWith("E")){

                    int panel_len = panel_center.getHeight();
                    int number_of_entry = 1;
                    try{
                        number_of_entry = Integer.parseInt(gate.getA().getFrom().substring(1)) - 1;
                    }
                    catch(Exception exp){
                        number_of_entry = 1;
                    }
                    end.x = 25;
                    end.y = panel_center.getHeight()/(entry_len+1)*(number_of_entry+1)-3;
                }
                else if(gate.getA().getFrom().startsWith("G")){
                    Gate from = getGateHashValue(gate.getA().getFrom());
                    end.x = from.getX()+55;
                    end.y = from.getY()+12;
                }
                else{

                }
                //g.drawLine(initial.x, initial.y, end.x, end.y);
                int x_medio = Math.abs(initial.x+end.x)/2;
                g.drawLine(initial.x, initial.y, x_medio, initial.y);
                g.drawLine(x_medio, initial.y, x_medio, end.y);
                g.drawLine(x_medio, end.y, end.x, end.y);
                
            }
            else{
                //--- input A ---------
                drawInput(gate.getA(),gate.getX(),gate.getY()+7,g);
                //--- input B ---------
                drawInput(gate.getB(),gate.getX(),gate.getY()+18,g);
                //---------------------
            }
        }

        /*****************************************
        * drawInput
        * @param gate
        * @param g
        * @return void
        * Draw one input line
        *****************************************/
        private void drawInput(Input input, int initial_x, int inicial_y, Graphics g) {
            //--- input A ---------
            Point initial = new Point(initial_x, inicial_y);
            Point end = new Point();
            if(input.getFrom().startsWith("E")){

                int panel_len = panel_center.getHeight();
                int number_of_entry = 1;
                try{
                    number_of_entry = Integer.parseInt(input.getFrom().substring(1)) - 1;
                }
                catch(Exception exp){
                    number_of_entry = 1;
                }
                end.x = 25;
                end.y = panel_center.getHeight()/(entry_len+1)*(number_of_entry+1)-3;
            }
            else if(input.getFrom().startsWith("G")){
                Gate from = getGateHashValue(input.getFrom());
                end.x = from.getX()+55;
                end.y = from.getY()+12;
            }
            
            //g.drawLine(initial.x, initial.y, end.x, end.y);
            int x_medio = Math.abs(initial.x+end.x)/2;
            g.drawLine(initial.x, initial.y, x_medio, initial.y);
            g.drawLine(x_medio, initial.y, x_medio, end.y);
            g.drawLine(x_medio, end.y, end.x, end.y);

        }

    }

}